package com.erp.service;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.zip.ZipInputStream;

import javax.annotation.Resource;

import org.activiti.bpmn.model.BpmnModel;
import org.activiti.engine.HistoryService;
import org.activiti.engine.IdentityService;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.delegate.Expression;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricVariableInstance;
import org.activiti.engine.impl.RepositoryServiceImpl;
import org.activiti.engine.impl.bpmn.behavior.UserTaskActivityBehavior;
import org.activiti.engine.impl.bpmn.diagram.ProcessDiagramGenerator;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.impl.pvm.delegate.ActivityBehavior;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.impl.task.TaskDefinition;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.Execution;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.apache.commons.beanutils.PropertyUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.erp.util.WorkflowUtils;

/**
 * 封装了Activiti接口的Service层，便于统一管理。
 * 
 * @author 陈凯
 * 
 */
@Service(value = "workflowService")
@Transactional
public class WorkflowService {

	/**
	 * 流程引擎对象
	 */
	@Resource(name = "processEngine")
	ProcessEngine processEngine;
	/**
	 * 仓库服务对象
	 * 
	 * ACT_RE*表操作接口
	 */
	@Resource(name = "repositoryService")
	RepositoryService repositoryService;
	/**
	 * 运行时的服务对象
	 * 
	 * ACT_RU_*表操作接口
	 */
	@Resource(name = "runtimeService")
	RuntimeService runtimeService;
	/**
	 * 任务服务对象
	 */
	@Resource(name = "taskService")
	TaskService taskService;
	/**
	 * 历史记录服务对象
	 * 
	 * ACT_HI_*表操作几口
	 */
	@Resource(name = "historyService")
	HistoryService historyService;

	/**
	 * ACT_ID_*表操作接口
	 */
	@Resource(name = "identityService")
	private IdentityService identityService;

	/**
	 * 通过开源的zip格式压缩包（包括 .jpdl.xml 和 .png两类文件）来部署流程定义
	 * 
	 * @param zis
	 * @return 返回流程定义
	 */
	public ProcessDefinition deployProcDef(ZipInputStream zis) {
		Deployment deployment = repositoryService//
				.createDeployment()//
				.addZipInputStream(zis)//
				.deploy();//
		ProcessDefinition procDef = repositoryService//
				.createProcessDefinitionQuery()//
				.deploymentId(deployment.getId())// 根据部署ID获取唯一的流程定义
				.singleResult();
		return procDef;
	}

	/**
	 * 获取用户代办任务的流程变量集和
	 * 
	 * @param username
	 * @return 有代办则返回流程变量集和，否则返回null
	 */
	public List<Map<String, Object>> listTaskVariables(String username) {
		List<Task> taskList = taskService.createTaskQuery()
				.taskAssignee(username.trim())//
				.list();//
		// 没有代办任务则返回空
		if (taskList == null || taskList.size() == 0) {
			return null;
		}
		List<Map<String, Object>> variableList = new ArrayList<Map<String, Object>>();
		Map<String, Object> variables = null;
		for (Task task : taskList) {

			variables = runtimeService
					.getVariables(task.getProcessInstanceId());
			variableList.add(variables);
		}
		return variableList;
	}

	/**
	 * 挂起某key的所有流程定义
	 * 
	 * @param procDefKey
	 */
	public void suspendProcDefByKey(String procDefKey) {
		repositoryService.suspendProcessDefinitionByKey(procDefKey);
	}

	/**
	 * 激活某key的所有流程定义
	 * 
	 * @param procDefKey
	 */
	public void activateProcDefByKey(String procDefKey) {
		repositoryService.activateProcessDefinitionByKey(procDefKey);
	}

	/**
	 * 删除某key的流程定义的部署对象的所有版本
	 * 
	 * @param procDefKey
	 */
	public void deleteDeploymentByKey(String procDefKey) {
		// 查询出指定key的所有版本的流程定义
		List<ProcessDefinition> procDefList = repositoryService//
				.createProcessDefinitionQuery()//
				.processDefinitionKey(procDefKey)//
				.list();
		System.out.println("key为" + procDefKey + "的流程定义共有" + procDefList.size()
				+ "个。");
		// 循环删除
		for (ProcessDefinition procDef : procDefList) {
			System.out.println("正在删除流程定义：" + procDef.getId());
			repositoryService.deleteDeployment(procDef.getDeploymentId());
		}
		System.out.println("删除完成。");
	}

	@Deprecated
	public void deleteDeploymentCascadeByKey(String procDefKey) {
		// 查询出指定key的所有版本的流程定义
		List<ProcessDefinition> procDefList = repositoryService//
				.createProcessDefinitionQuery()//
				.processDefinitionKey(procDefKey)//
				.list();
		System.out.println("key为" + procDefKey + "的流程定义共有" + procDefList.size()
				+ "个。");
		// 循环删除
		for (ProcessDefinition procDef : procDefList) {
			System.out.println("正在删除流程定义：" + procDef.getId());
			repositoryService
					.deleteDeploymentCascade(procDef.getDeploymentId());
		}
		System.out.println("删除完成。");
	}

	/**
	 * 获取所有已部署的流程定义的最新版本
	 * 
	 * @return 最新版本的流程定义列表
	 */
	@Deprecated
	public ArrayList<ProcessDefinition> findAllLastProcDef() {
		List<ProcessDefinition> procDefList = repositoryService
				.createProcessDefinitionQuery()// 创建流程定义查询对象
				.orderByProcessDefinitionVersion()//
				.asc()// 按版本升序排列
				.list();

		// 利用Map的key不可重复的特性来获取最高版本
		Map<String, ProcessDefinition> procDefMap = new HashMap<String, ProcessDefinition>();
		for (ProcessDefinition procDef : procDefList) {
			procDefMap.put(procDef.getKey(), procDef);
		}

		return new ArrayList<ProcessDefinition>(procDefMap.values());
	}

	/**
	 * 下载已部署的流程定义图片到指定路径
	 * 
	 * @param procDefId
	 *            流程定义的ID，格式为（key : version : 随机数）
	 * @param imagPath
	 * @return 返回图片的相对路径
	 */
	public String downloadProcessDefinitionImageTo(String procDefId,
			String imagPath) {

		InputStream is = repositoryService.getProcessDiagram(procDefId);
		OutputStream os = null;
		try {
			File imgFile = new File(imagPath);
			os = new FileOutputStream(imgFile);
			int count = 0;
			while ((count = is.read()) != -1) {
				os.write(count);
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				is.close();
				os.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

		return imagPath.substring(imagPath.indexOf("images"));
	}

	/**
	 * 高亮执行中的任务节点
	 * 
	 * @param procDefId
	 * @param executionId
	 * @return
	 */
	public InputStream getProcInstImage(String procDefId, String executionId) {
		BpmnModel bpmnModel = repositoryService.getBpmnModel(procDefId);
		List<String> highLightedActivities = runtimeService
				.getActiveActivityIds(executionId);
		InputStream is = ProcessDiagramGenerator.generateDiagram(bpmnModel,
				"png", highLightedActivities);
		return is;
	}

	/**
	 * 使用指定key的流程定义的最新版本来启动一个新的流程实例，同时设置流程变量，返回流程实例的ID
	 * 
	 * @param procDefKey
	 * @param variables
	 * @return
	 */
	public String startProcessInstanceByKey(String procDefKey,
			Map<String, Object> variables) {
		ProcessInstance pi = runtimeService//
				.startProcessInstanceByKey(procDefKey, variables);
		return pi.getId();
	}

	/**
	 * 使用指定key的流程定义的最新版本来启动一个新的流程实例，返回流程实例的ID
	 * 
	 * @param procDefKey
	 * @return
	 */
	public String startProcessInstanceByKey(String procDefKey) {
		ProcessInstance pi = runtimeService//
				.startProcessInstanceByKey(procDefKey);
		return pi.getId();
	}

	/**
	 * 根据流程实例的ID获取任务，根据任务来获取任务ID，根据任务ID来完成任务
	 * 
	 * @param procInstId
	 */
	public void completeTaskByProcInstId(String procInstId) {
		Task task = taskService.createTaskQuery()//
				.executionId(procInstId)//
				.singleResult();
		taskService.complete(task.getId());
	}

	/**
	 * 完成任务同时设置全局流程变量
	 * 
	 * @param procInstId
	 * @param variables
	 */
	public void completeTaskByProcInstId(String procInstId,
			Map<String, Object> variables) {
		Task task = taskService.createTaskQuery()//
				.executionId(procInstId)//
				.singleResult();
		String taskId = task.getId();
		taskService.complete(taskId, variables);
	}

	/**
	 * 完成任务同时设置任务完成人
	 * 
	 * @param procInstId
	 * @param userName
	 */
	public void completeTaskByProcInstId(String procInstId, String userName) {
		Task task = taskService.createTaskQuery()//
				.executionId(procInstId)//
				.singleResult();
		String taskId = task.getId();
		taskService.setAssignee(taskId, userName);
		taskService.complete(taskId);
	}

	/**
	 * 根据流程实例的ID来获取该实例的当前任务的执行人
	 * 
	 * @param procInstId
	 * @return
	 */
	public String getAssignee(String procInstId) {
		Task task = taskService.createTaskQuery()//
				.executionId(procInstId)//
				.singleResult();
		if (task != null) {
			return task.getAssignee();
		} else {
			return null;
		}
	}

	/**
	 * 根据运行中或是历史的流程执行ID（一般为流程实例ID）获取指定名称的全局流程变量
	 * 
	 * @param executionId
	 * @param variableName
	 * @return
	 */
	public Object getVariable(String executionId, String variableName) {
		Object obj = null;
		Execution execution = runtimeService//
				.createExecutionQuery()//
				.executionId(executionId)//
				.singleResult();
		if (execution != null) {
			obj = runtimeService.getVariable(executionId, variableName);
			System.out.println("已获取运行中流程实例的" + variableName + "变量.");
		} else {
			HistoricVariableInstance hisVariableInst = historyService
					.createHistoricVariableInstanceQuery()//
					.processInstanceId(executionId)//
					.variableName(variableName)//
					.singleResult();
			obj = hisVariableInst.getValue();
			System.out.println("已获取流程实例历史中的" + variableName + "变量.");
		}
		return obj;
	}

	/**
	 * 获取指定流程实例的流程变量Map集合
	 * 
	 * @param procInstId
	 * @return
	 */
	public Map<String, Object> findVariable(String procInstId) {
		return runtimeService.getVariables(procInstId);
	}

	/**
	 * 在指定的任务上设置流程变量。
	 * 
	 * @param taskId
	 * @param variableName
	 * @param value
	 */
	public void setVariableLocal(String taskId, String variableName,
			Object value) {
		taskService.setVariableLocal(taskId, variableName, value);
	}

	/**
	 * 在当前任务上设置流程变量。
	 * 
	 * @param procInstId
	 * @param variableName
	 * @param value
	 */
	public void setVariable2CurrentTask(String procInstId, String variableName,
			Object value) {
		Task task = taskService.createTaskQuery()//
				.executionId(procInstId)//
				.singleResult();//
		taskService.setVariableLocal(task.getId(), variableName, value);
	}

	/**
	 * 设置流程变量
	 * 
	 * @param executionId
	 * @param variables
	 */
	public void setVariable(String executionId, String variableName,
			Object value) {
		runtimeService.setVariable(executionId, variableName, value);
	}

	/**
	 * 设置全局流程变量
	 * 
	 * @param executionId
	 * @param variables
	 */
	public void setVariable(String executionId, Map<String, Object> variables) {
		runtimeService.setVariables(executionId, variables);
	}

	/**
	 * 根据流程实例的ID获得流程定义的key
	 * 
	 * @param procInstId
	 * @return
	 */
	public String getProcDefKeyByProcInstId(String procInstId) {
		// 根据流程实例的ID获取流程定义的ID(若为空则从流程实例历史获取流程定义)
		String procDefId = getProcDefIdByProcInstId(procInstId);
		if (procDefId == null) {
			procDefId = getProcDefIdByProcInstIdFromHistory(procInstId);
		}
		// 根据流程定义的ID返回流程定义的KEY
		ProcessDefinition procDef = repositoryService//
				.createProcessDefinitionQuery()//
				.processDefinitionId(procDefId)//
				.singleResult();
		return procDef.getKey();
	}

	/**
	 * 根据流程实例的ID获取流程定义的ID
	 * 
	 * @param procInstId
	 * @return
	 */
	public String getProcDefIdByProcInstId(String procInstId) {
		String procDefId = null;
		ProcessInstance procInst = runtimeService//
				.createProcessInstanceQuery()//
				.processInstanceId(procInstId)//
				.singleResult();
		if (procInst != null) {
			procDefId = procInst.getProcessDefinitionId();
		}
		return procDefId;
	}

	/**
	 * 根据流程实例的ID从流程实例历史获取流程定义的ID
	 * 
	 * @param procInstId
	 * @return
	 */
	public String getProcDefIdByProcInstIdFromHistory(String procInstId) {
		String procDefId = null;
		HistoricProcessInstance hisProcInst = historyService//
				.createHistoricProcessInstanceQuery()//
				.processInstanceId(procInstId)//
				.singleResult();
		if (hisProcInst != null) {
			procDefId = hisProcInst.getProcessDefinitionId();
		}
		return procDefId;
	}

	/**
	 * 获取当前任务的ID
	 * 
	 * @param procInstId
	 * @return
	 */
	public String getCurrentTaskId(String procInstId) {
		Task task = taskService.createTaskQuery()//
				.executionId(procInstId)//
				.singleResult();//
		return task.getId();
	}

	/**
	 * 流程跟踪图
	 * 
	 * @param procInstId
	 *            流程实例ID
	 * @return 封装了各种节点信息
	 */
	public List<Map<String, Object>> traceProcess(String procInstId)
			throws Exception {
		Execution execution = runtimeService.createExecutionQuery()
				.executionId(procInstId).singleResult();// 执行实例
		Object property = PropertyUtils.getProperty(execution, "activityId");
		String activityId = "";
		if (property != null) {
			activityId = property.toString();
		}
		ProcessInstance processInstance = runtimeService//
				.createProcessInstanceQuery()//
				.processInstanceId(procInstId)//
				.singleResult();
		ProcessDefinitionEntity processDefinition = (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService)
				.getDeployedProcessDefinition(processInstance
						.getProcessDefinitionId());
		List<ActivityImpl> activitiList = processDefinition.getActivities();// 获得当前任务的所有节点

		List<Map<String, Object>> activityInfos = new ArrayList<Map<String, Object>>();
		for (ActivityImpl activity : activitiList) {

			boolean currentActiviti = false;
			String id = activity.getId();

			// 当前节点
			if (id.equals(activityId)) {
				currentActiviti = true;
			}

			Map<String, Object> activityImageInfo = packageSingleActivitiInfo(
					activity, processInstance, currentActiviti);

			activityInfos.add(activityImageInfo);
		}

		return activityInfos;
	}

	/**
	 * 封装输出信息，包括：当前节点的X、Y坐标、变量信息、任务类型、任务描述
	 * 
	 * @param activity
	 * @param procInst
	 * @param currentActiviti
	 * @return
	 */
	private Map<String, Object> packageSingleActivitiInfo(
			ActivityImpl activity, ProcessInstance procInst,
			boolean currentActiviti) throws Exception {
		Map<String, Object> vars = new HashMap<String, Object>();
		Map<String, Object> activityInfo = new HashMap<String, Object>();
		activityInfo.put("currentActiviti", currentActiviti);
		setPosition(activity, activityInfo);
		setWidthAndHeight(activity, activityInfo);

		Map<String, Object> properties = activity.getProperties();
		vars.put("任务类型",
				WorkflowUtils.parseToZhType(properties.get("type").toString()));

		ActivityBehavior activityBehavior = activity.getActivityBehavior();
		if (activityBehavior instanceof UserTaskActivityBehavior) {

			Task currentTask = null;

			/*
			 * 当前节点的task
			 */
			if (currentActiviti) {
				currentTask = getCurrentTaskInfo(procInst);
			}

			/*
			 * 当前任务的分配角色
			 */
			UserTaskActivityBehavior userTaskActivityBehavior = (UserTaskActivityBehavior) activityBehavior;
			TaskDefinition taskDefinition = userTaskActivityBehavior
					.getTaskDefinition();
			Set<Expression> candidateGroupIdExpressions = taskDefinition
					.getCandidateGroupIdExpressions();
			if (!candidateGroupIdExpressions.isEmpty()) {

				// 任务的处理角色
				setTaskGroup(vars, candidateGroupIdExpressions);

				// 当前处理人
				if (currentTask != null) {
					setCurrentTaskAssignee(vars, currentTask);
				}
			}
		}

		vars.put("节点说明", properties.get("documentation"));

		String description = activity.getProcessDefinition().getDescription();
		vars.put("描述", description);

		activityInfo.put("vars", vars);
		return activityInfo;
	}

	private void setTaskGroup(Map<String, Object> vars,
			Set<Expression> candidateGroupIdExpressions) {
		String roles = "";
		for (Expression expression : candidateGroupIdExpressions) {
			String expressionText = expression.getExpressionText();
			String roleName = identityService//
					.createGroupQuery()//
					.groupId(expressionText)//
					.singleResult()//
					.getName();//
			roles += roleName;
		}
		vars.put("任务所属角色", roles);
	}

	/**
	 * 设置当前处理人信息
	 * 
	 * @param vars
	 * @param currentTask
	 */
	private void setCurrentTaskAssignee(Map<String, Object> vars,
			Task currentTask) {
		String assignee = currentTask.getAssignee();
		if (assignee != null) {
			org.activiti.engine.identity.User assigneeUser = identityService//
					.createUserQuery()//
					.userId(assignee)//
					.singleResult();
			String userInfo = assigneeUser.getFirstName() + " "
					+ assigneeUser.getLastName();
			vars.put("当前处理人", userInfo);
		}
	}

	/**
	 * 获取当前节点信息
	 * 
	 * @param processInstance
	 * @return
	 */
	private Task getCurrentTaskInfo(ProcessInstance processInstance) {
		Task currentTask = null;
		try {
			String activitiId = (String) PropertyUtils.getProperty(
					processInstance, "activityId");

			currentTask = taskService//
					.createTaskQuery()//
					.processInstanceId(processInstance.getId())//
					.taskDefinitionKey(activitiId).singleResult();

		} catch (Exception e) {
			e.printStackTrace();
		}
		return currentTask;
	}

	/**
	 * 设置宽度、高度属性
	 * 
	 * @param activity
	 * @param activityInfo
	 */
	private void setWidthAndHeight(ActivityImpl activity,
			Map<String, Object> activityInfo) {
		activityInfo.put("width", activity.getWidth());
		activityInfo.put("height", activity.getHeight());
	}

	/**
	 * 设置坐标位置
	 * 
	 * @param activity
	 * @param activityInfo
	 */
	private void setPosition(ActivityImpl activity,
			Map<String, Object> activityInfo) {
		activityInfo.put("x", activity.getX());
		activityInfo.put("y", activity.getY());
	}

}
